package com.clp.protocol.iec104.iec104.client.state;

import com.clp.protocol.core.exception.FailedToSendFrameException;
import com.clp.protocol.iec104.iec104.apdu.Apdu;
import com.clp.protocol.iec104.iec104.apdu.IApdu;
import com.clp.protocol.iec104.iec104.client.async.MasterPromise;
import com.clp.protocol.iec104.iec104.client.async.sendapdu.SendTotalCall100Res;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

/**
 * 主站的总召唤100状态
 */
@Slf4j
public class TotalCall100MasterState extends AbstractMasterState {

    private enum State {
        TOTAL_CALL_100,
        TOTAL_CALL_100_ACK,
        TOTAL_CALL_100_END;
    }

    private final int periodSeconds;

    private volatile State state;
    private volatile Date lastTotalCall100Time;
    private volatile Date lastTotalCall100AckTime;
    private volatile Date lastTotalCall100EndTime;

    private final Vector<MasterPromise<SendTotalCall100Res>> promises;

    protected TotalCall100MasterState(InMasterStateManager stateManager, int periodSeconds) {
        super(stateManager);
        this.periodSeconds = periodSeconds;
        this.promises = new Vector<>();
        this.resetState();
    }

    @Override
    protected Future<Void> resetState() {
        Promise<Void> promise = createPromise(Void.class);
        this.state = State.TOTAL_CALL_100_END;
        this.lastTotalCall100Time = new Date();
        this.lastTotalCall100AckTime = new Date();
        this.lastTotalCall100EndTime = new Date();
        this.promises.clear();
        return promise.setSuccess(null);
    }

    @Override
    protected Future<Void> refreshState() {
        return resetState();
    }

    @Override
    public Apdu updateStateBySending(Apdu apdu) {
        if (apdu == null || !apdu.isIType()) return apdu;
        final IApdu iApdu = apdu.castToIType();
        switch (iApdu.getIAsdu().getTypeTag()) {
            case C_IC_NA_1: // 总召唤100
                switch (iApdu.getIAsdu().getCot().getCause()) {
                    case COT_ACT: // 激活
                        if (state == State.TOTAL_CALL_100_END) {
                            state = State.TOTAL_CALL_100;
                            promises.forEach(promise -> promise.getRes().setSendSuccess(true));
                            lastTotalCall100Time = new Date();
                        } else {
                            promises.forEach(promise -> {
                                promise.getRes().setSendSuccess(false).setRecvTotalCall100Ack(false).setRecvTotalCall100End(false);
                                promise.setFailure(new FailedToSendFrameException(iApdu));
                            });
                            promises.clear();
                            log.warn("已经在进行总召唤，放弃本次总召唤命令！");
                            return null;
                        }
                        break;
                }
                break;
        }

        return apdu;
    }

    @Override
    public Apdu updateStateByRecving(Apdu apdu) {
        if (apdu == null || !apdu.isIType()) return apdu;
        IApdu iApdu = apdu.castToIType();
        switch (iApdu.getIAsdu().getTypeTag()) {
            case C_IC_NA_1: // 总召唤100
                switch (iApdu.getIAsdu().getCot().getCause()) {
                    case COT_ACTCON: // 确认激活
                        if (state == State.TOTAL_CALL_100) {
                            promises.forEach(promise -> promise.getRes().setRecvTotalCall100Ack(true));
                            state = State.TOTAL_CALL_100_ACK;
                            lastTotalCall100AckTime = new Date();
                        } else {
                            log.warn("接收到无效的总召唤确认报文！");
                        }
                        break;
                    case COT_ACTTERM: // 激活终止
                        if (state == State.TOTAL_CALL_100_ACK) {
                            promises.forEach(promise -> {
                                promise.getRes().setRecvTotalCall100End(true);
                                promise.setSuccess();
                            });
                            promises.clear();
                            state = State.TOTAL_CALL_100_END;
                            lastTotalCall100EndTime = new Date();
                        } else {
                            log.warn("接收到无效的总召唤确认终止报文！");
                        }
                        break;
                }
                break;
        }

        return apdu;
    }

    @Override
    protected ScheduledTask getScheduledTask() {
        return new ScheduledTask(0, 1, TimeUnit.SECONDS) {
            @Override
            public void run() {
                if (!getStateManager().isRecvedInitEnd()) {
                    return;
                }
                switch (state) {
                    case TOTAL_CALL_100:
                        if (new Date().getTime() - lastTotalCall100Time.getTime() > 1000 * 3) {
                            promises.forEach(promise -> {
                                promise.getRes().setRecvTotalCall100Ack(false).setRecvTotalCall100End(false);
                                promise.setFailure(new RuntimeException("发送总召唤100的3秒内没有收到确认报文，放弃本次总召唤！"));
                            });
                            promises.clear();
                            state = State.TOTAL_CALL_100_END;
                            lastTotalCall100EndTime = new Date();
                            log.warn("发送总召唤100的3秒内没有收到确认报文，放弃本次总召唤！");
                        }
                        break;
                    case TOTAL_CALL_100_ACK:
                        if (new Date().getTime() - lastTotalCall100AckTime.getTime() > 1000 * 3) {
                            promises.forEach(promise -> {
                                promise.getRes().setRecvTotalCall100End(false);
                                promise.setSuccess(); // TODO 默认为成功了
                            });
                            promises.clear();
                            state = State.TOTAL_CALL_100_END;
                            lastTotalCall100EndTime = new Date();
                            log.warn("接收到总召唤100确认的3秒内没有收到召唤终止报文，默认本次召唤结束！");
                        }
                        break;
                    case TOTAL_CALL_100_END:
                        if (new Date().getTime() - lastTotalCall100EndTime.getTime() > 1000L * periodSeconds) {
                            getStateManager().getInMaster().getApduSender().sendIApduOfTotalCall100();
                            log.info("周期性发送总召唤100");
                        }
                        break;
                }
            }
        };
    }

    @Override
    @SuppressWarnings("unchecked")
    public <V> MasterPromise<V> register(MasterPromise<V> sendPromise) {
        if (sendPromise == null || sendPromise.getRes() == null) return sendPromise;
        if (sendPromise.getRes() instanceof SendTotalCall100Res) {
            promises.add(((MasterPromise<SendTotalCall100Res>) sendPromise));
            return null;
        }
        return sendPromise;
    }

    public boolean isDoingTotalCall100() {
        return state != State.TOTAL_CALL_100_END;
    }
}
